using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Xml;
using System.Threading;
using System.Security.Cryptography;

namespace cb0tServer
{
    class Passwords
    {
        private static Mutex mutex = new Mutex();
        private static List<PasswordItem> items = new List<PasswordItem>();

        public static int IsValidPassword(String text)
        {
            foreach (PasswordItem p in items)
                if (p.password == text)
                    return p.level;

            return -1;
        }

        public static bool IsAutoPasswordLogin(UserObject userobj, byte[] sha1pass)
        {
            SHA1Managed _sha = new SHA1Managed();
            List<byte> _builder = new List<byte>();
            byte[] cookie = BitConverter.GetBytes(userobj.admincookie);
            byte[] _pass, _result;

            // check additional host level password
            try
            {
                if (CoreSettings.additional_host_pass.Length > 0)
                {
                    _sha = new SHA1Managed();
                    _pass = _sha.ComputeHash(Encoding.UTF8.GetBytes(CoreSettings.additional_host_pass));
                    _builder.Clear();
                    _builder.AddRange(cookie);
                    _builder.AddRange(CoreSettings.room_external_ip.GetAddressBytes());
                    _builder.AddRange(_pass);
                    _sha = new SHA1Managed();
                    _result = _sha.ComputeHash(_builder.ToArray());

                    if (ByteArrayComparer(_result, sha1pass))
                    {
                        userobj.admin = true;
                        userobj.level = 5;
                        userobj.host = true;
                        UserPool.Broadcast(ServerOutboundPackets.AnnoucePacket(StringTemplate.GetString(11).Replace("+n", userobj.name).Replace("+l", "5")));
                        AdminLog.WriteLine(userobj.name + " " + userobj.physical_ip.ToString() + " logged in with password " + CoreSettings.additional_host_pass, true);
                        return true;
                    }
                }
            }
            catch { }

            // check additional host level password
            try
            {
                if (CoreSettings.additional_host_pass.Length > 0)
                {
                    _sha = new SHA1Managed();
                    _pass = _sha.ComputeHash(Encoding.UTF8.GetBytes(CoreSettings.additional_host_pass));
                    _builder.Clear();
                    _builder.AddRange(cookie);
                    _builder.AddRange(userobj.external_ip.GetAddressBytes());
                    _builder.AddRange(_pass);
                    _sha = new SHA1Managed();
                    _result = _sha.ComputeHash(_builder.ToArray());

                    if (ByteArrayComparer(_result, sha1pass))
                    {
                        userobj.admin = true;
                        userobj.level = 5;
                        userobj.host = true;
                        UserPool.Broadcast(ServerOutboundPackets.AnnoucePacket(StringTemplate.GetString(11).Replace("+n", userobj.name).Replace("+l", "5")));
                        AdminLog.WriteLine(userobj.name + " " + userobj.physical_ip.ToString() + " logged in with password " + CoreSettings.additional_host_pass, true);
                        return true;
                    }
                }
            }
            catch { }

            // check regular passwords
            try
            {
                foreach (PasswordItem pass in items)
                {
                    _sha = new SHA1Managed();
                    _pass = _sha.ComputeHash(Encoding.UTF8.GetBytes(pass.password));
                    _builder.Clear();
                    _builder.AddRange(cookie);
                    _builder.AddRange(CoreSettings.room_external_ip.GetAddressBytes());
                    _builder.AddRange(_pass);
                    _sha = new SHA1Managed();
                    _result = _sha.ComputeHash(_builder.ToArray());

                    if (ByteArrayComparer(_result, sha1pass))
                    {
                        userobj.admin = true;
                        userobj.level = (byte)pass.level;
                        UserPool.Broadcast(ServerOutboundPackets.AnnoucePacket(StringTemplate.GetString(11).Replace("+n", userobj.name).Replace("+l", pass.level.ToString())));
                        AdminLog.WriteLine(userobj.name + " " + userobj.physical_ip.ToString() + " logged in with password " + pass.password, true);
                        return true;
                    }
                }
            }
            catch { }

            // check regular passwords
            try
            {
                foreach (PasswordItem pass in items)
                {
                    _sha = new SHA1Managed();
                    _pass = _sha.ComputeHash(Encoding.UTF8.GetBytes(pass.password));
                    _builder.Clear();
                    _builder.AddRange(cookie);
                    _builder.AddRange(userobj.external_ip.GetAddressBytes());
                    _builder.AddRange(_pass);
                    _sha = new SHA1Managed();
                    _result = _sha.ComputeHash(_builder.ToArray());

                    if (ByteArrayComparer(_result, sha1pass))
                    {
                        userobj.admin = true;
                        userobj.level = (byte)pass.level;
                        UserPool.Broadcast(ServerOutboundPackets.AnnoucePacket(StringTemplate.GetString(11).Replace("+n", userobj.name).Replace("+l", pass.level.ToString())));
                        AdminLog.WriteLine(userobj.name + " " + userobj.physical_ip.ToString() + " logged in with password " + pass.password, true);
                        return true;
                    }
                }
            }
            catch { }

            return false;
        }

        private static bool ByteArrayComparer(byte[] arr1, byte[] arr2)
        {
            if (arr1.Length != arr2.Length)
                return false;

            for (int i = 0; i < arr2.Length; i++)
                if (arr1[i] != arr2[i])
                    return false;

            return true;
        }

        public static bool AddPassword(String text, int level)
        {
            foreach (PasswordItem p in items)
                if (p.password == text)
                    return false;

            items.Add(new PasswordItem(text, level));
            Save();
            return true;
        }

        public static bool RemovePassword(String text)
        {
            int counter = -1;

            for (int i = 0; i < items.Count; i++)
            {
                if (items[i].password == text)
                {
                    counter = i;
                    break;
                }
            }

            if (counter == -1) return false;

            items.RemoveAt(counter);
            Save();
            return true;
        }

        public static String[] GetPasswords()
        {
            if (items.Count > 0)
            {
                List<String> dump = new List<String>();

                foreach (PasswordItem p in items)
                    dump.Add(p.password);

                return dump.ToArray();
            }

            return null;
        }

        public static void Load()
        {
            items.Clear();

            try
            {
                FileStream f = new FileStream(sb0t_winGUI.RunOnceFileUpdater.sbotpath + "data/passwords.xml", FileMode.Open);
                XmlReader xml = XmlReader.Create(new StreamReader(f));

                xml.MoveToContent();
                xml.ReadSubtree().ReadToFollowing("passwords");

                while (xml.ReadToFollowing("password"))
                {
                    xml.ReadSubtree().ReadToFollowing("data");
                    String str = xml.ReadElementContentAsString();
                    List<byte> buf = new List<byte>(Convert.FromBase64String(str));
                    int level;

                    if (buf[0] > 0 && buf[0] < 5)
                    {
                        level = buf[0];
                        buf.RemoveAt(0);

                    }
                    else
                    {
                        level = 4;
                    }

                    str = Encoding.UTF8.GetString(buf.ToArray());
                    items.Add(new PasswordItem(str, level));
                }

                xml.Close();
                f.Flush();
                f.Close();
            }
            catch { }
        }

        public static void Save()
        {
            mutex.WaitOne();

            try
            {
                XmlWriterSettings appearance = new XmlWriterSettings();
                appearance.Indent = true;
                XmlWriter xml = XmlWriter.Create(sb0t_winGUI.RunOnceFileUpdater.sbotpath + "data/passwords.xml", appearance);

                xml.WriteStartDocument();
                xml.WriteStartElement("passwords");

                for (int i = 0; i < items.Count; i++)
                {
                    xml.WriteStartElement("password");
                    List<byte> buf = new List<byte>();
                    buf.Add((byte)items[i].level);
                    buf.AddRange(Encoding.UTF8.GetBytes(items[i].password));
                    xml.WriteElementString("data", Convert.ToBase64String(buf.ToArray()));
                    xml.WriteEndElement();
                }

                xml.WriteEndElement();
                xml.WriteEndDocument();
                xml.Flush();
                xml.Close();
            }
            catch { }

            mutex.ReleaseMutex();
        }
    }

    class PasswordItem
    {
        public String password;
        public int level;

        public PasswordItem() { }

        public PasswordItem(String password, int level)
        {
            this.password = password;
            this.level = level;
        }
    }
}
